﻿using GWDataCenter;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace GWMergeTerminal.STD
{
    internal class DeviceMessageSubscriber
    {

        /// <summary>
        /// 遥信Data
        /// </summary>
        public Dictionary<string, MqYXMessage> yXData = new Dictionary<string, MqYXMessage>();
        /// <summary>
        /// 电流
        /// </summary>
        public Dictionary<string, MqDLMessage> dLData = new Dictionary<string, MqDLMessage>();
        /// <summary>
        /// 电压
        /// </summary>
        public Dictionary<string, MqDYMessage> dYData = new Dictionary<string, MqDYMessage>();
        /// <summary>
        /// 功率
        /// </summary>
        public Dictionary<string, MqGLMessage> gLData = new Dictionary<string, MqGLMessage>();
        public Dictionary<string, MqStateMessage> steData = new Dictionary<string, MqStateMessage>();
        /// <summary>
        /// 获取指令下发响应
        /// </summary>
        public Dictionary<string, MqCmdRspMessage> deviceCmdRsp = new Dictionary<string, MqCmdRspMessage>();

        /// <summary>
        /// 连接状态
        /// </summary>
        public bool IsConnected { get { return client.IsConnected; } }

        /// <summary>
        /// MQ客户端
        /// </summary>
        private IMqttClient client = new MqttFactory().CreateMqttClient();
        /// <summary>
        /// 主题HashSet
        /// </summary>
        private HashSet<string> _topicCache = new HashSet<string>();

        /// <summary>
        /// 设备消息订阅者重载函数
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="user"></param>
        /// <param name="pwd"></param>
        /// <param name="targetEquipno"></param>
        public DeviceMessageSubscriber(string ip, int port, string user, string pwd, string targetEquipno)
        {

            client.UseApplicationMessageReceivedHandler(MessageReceivedHandler);

            client.UseConnectedHandler((args) =>
            {
                Utils.LogDebug($"connected {ip}:{port},{args.AuthenticateResult.ResultCode}");
                //重新订阅所有topic
                foreach (var item in _topicCache)
                {
                    Subscribe(item);
                }
            });

            client.UseDisconnectedHandler((args) =>
            {
                Task.Delay(1000).ContinueWith(t => Connect(ip, port, user, pwd));
            });

            Connect(ip, port, user, pwd);
        }
        /// <summary>
        /// 接收到订阅消息
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private Task MessageReceivedHandler(MqttApplicationMessageReceivedEventArgs e)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    var payloadStr = Utils.DeserializeObject<object>(e.ApplicationMessage.Payload);
                    string[] str = e.ApplicationMessage.Topic.Split('/');
                    string equipNo = str.LastOrDefault();
                    LogDebug($"------------------------------------------------Start---{DateTime.Now}---------------------------------------------<<<\n推送的主题为：{e.ApplicationMessage.Topic}\n推送的数据消息体为：{payloadStr}\n检查当前订阅的所有主题...");
                    LogDebug($"该设备【设备上线】的主题为：{TopicString.DeviceState + equipNo}");
                    LogDebug($"该设备【数据上报】的主题为：{TopicString.DeviceDataUp + equipNo}");
                    LogDebug($"该设备【数据指令下发响应】订阅的主题为：{TopicString.DeviceActionResult + equipNo}\n>>>开始尝试序列化....");
                    //消息主题为数据上报
                    if (e.ApplicationMessage.Topic == TopicString.DeviceDataUp + equipNo)
                    {
                        var payload = Utils.DeserializeObject<object>(e.ApplicationMessage.Payload);
                        //LogDebug($"{equipNo}获取数据的主题为：{e.ApplicationMessage.Topic}");
                        //LogDebug($"body:serviceId，或者body:state才会被序列化，{equipNo}获取数据的消息体为：{payload}");
                        LogDebug($"body:serviceId，或者body:state才会被序列化");

                        var mqttDatas = Utils.DeserializeObject<JObject>(e.ApplicationMessage.Payload);
                        var key = mqttDatas.SelectToken("body").SelectToken("serviceId")?.ToString();//数据上报
                        //var key2 = mqttDatas.SelectToken("body").SelectToken("state")?.ToString();//设备上线
                        if (!string.IsNullOrEmpty(key))
                        {
                            //添加数据
                            switch (key)
                            {
                                case "Analog_Voltage"://电压
                                    var mqDYMessage = JsonConvert.DeserializeObject<MqDYMessage>(mqttDatas.ToString());
                                    if (!dYData.ContainsKey(equipNo))
                                    {
                                        dYData.Add(equipNo, mqDYMessage);
                                        LogDebug($"插入电压数据成功...【End-At{DateTime.Now}】");
                                       // LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    else
                                    {
                                        dYData[equipNo] = mqDYMessage;
                                        LogDebug($"更新电压数据成功...【End-At{DateTime.Now}】");
                                        //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    break;
                                case "Analog_Current"://电流
                                    var mqDLMessage = JsonConvert.DeserializeObject<MqDLMessage>(mqttDatas.ToString());
                                    if (!dLData.ContainsKey(equipNo))
                                    {
                                        dLData.Add(equipNo, mqDLMessage);
                                        LogDebug($"插入电流数据成功...【End-At{DateTime.Now}】");
                                       // LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    else
                                    {
                                        dLData[equipNo] = mqDLMessage;
                                        LogDebug($"更新电流数据成功...【End-At{DateTime.Now}】");
                                        //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    break;
                                case "Analog_Power"://功率
                                    var mqGLMessage = JsonConvert.DeserializeObject<MqGLMessage>(mqttDatas.ToString());
                                    if (!gLData.ContainsKey(equipNo))
                                    {
                                        gLData.Add(equipNo, mqGLMessage);
                                        LogDebug($"插入功率数据成功...【End-At{DateTime.Now}】");
                                        //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    else
                                    {
                                        gLData[equipNo] = mqGLMessage;
                                        LogDebug($"更新功率数据成功...【End-At{DateTime.Now}】");
                                        //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    break;
                                case "Discrete_Alarm"://遥信
                                    var mqYXMessage = JsonConvert.DeserializeObject<MqYXMessage>(mqttDatas.ToString());
                                    if (!yXData.ContainsKey(equipNo))
                                    {
                                        yXData.Add(equipNo, mqYXMessage);
                                        LogDebug($"插入遥信数据成功...【End-At{DateTime.Now}】");
                                        //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    else
                                    {
                                        yXData[equipNo] = mqYXMessage;
                                        LogDebug($"更新遥信数据成功...【End-At{DateTime.Now}】");
                                        //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                                    }
                                    break;
                            }
                        }

                    }
                    //指令执行结果
                    else if (e.ApplicationMessage.Topic == TopicString.DeviceActionResult + equipNo)
                    {
                        var cmdRspStr = Utils.DeserializeObject<object>(e.ApplicationMessage.Payload);
                        LogDebug($"指令执行的响应结果为：{cmdRspStr}\n>>>>>尝试对数据进行序列化");
                        try
                        {
                            var device12 = Utils.DeserializeObject<object>(e.ApplicationMessage.Payload);

                            var device = Utils.DeserializeObject<MqCmdRspMessage>(e.ApplicationMessage.Payload);

                            if (!deviceCmdRsp.ContainsKey(equipNo))
                            {
                                deviceCmdRsp.Add(equipNo, new MqCmdRspMessage()
                                {
                                    Body = device.Body,
                                    Token = device.Token,
                                    Timestamp = device.Timestamp
                                });
                                LogDebug($"插入数据成功...【End-At{DateTime.Now}】");
                                //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                            }
                            else
                            {
                                deviceCmdRsp[equipNo] = new MqCmdRspMessage()
                                {
                                    Body = device.Body,
                                    Token = device.Token,
                                    Timestamp = device.Timestamp
                                };
                                LogDebug($"更新数据成功...【End-At{DateTime.Now}】");
                                //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                            }
                        }
                        catch (Exception ex)
                        {
                            LogDebug($"序列化失败...请检查错误信息：\n{ex.Message}");
                            //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");


                            throw;
                        }
                    }
                    //设备上线
                    else if (e.ApplicationMessage.Topic == TopicString.DeviceState + equipNo)
                    {

                        var payload = Utils.DeserializeObject<object>(e.ApplicationMessage.Payload);
                        LogDebug($"{equipNo}获取数据的主题为：{e.ApplicationMessage.Topic}");
                        //LogDebug($"结构为body:state才会被序列化，{equipNo}获取数据的消息体为：{payload}");

                        var mqttDatas = Utils.DeserializeObject<JObject>(e.ApplicationMessage.Payload);
                        var key2 = mqttDatas.SelectToken("body").SelectToken("state")?.ToString();//设备上线

                        if (!string.IsNullOrEmpty(key2))
                        {
                            //var mqSTEMessage = JsonConvert.DeserializeObject<MqStateMessage>(mqttDatas.ToString());
                            MqStateMessage mqStateMessage = new MqStateMessage();
                            StateBody body = new StateBody();
                            mqStateMessage.Token = mqttDatas.SelectToken("token").ToString();
                            mqStateMessage.Timestamp = mqttDatas.SelectToken("timestamp").ToString();
                            mqStateMessage.Body = body;
                            mqStateMessage.Body.State = mqttDatas.SelectToken("body").SelectToken("state")?.ToString();
                            mqStateMessage.Body.AppName = mqttDatas.SelectToken("body").SelectToken("appName")?.ToString();
                            mqStateMessage.Body.AppIp = mqttDatas.SelectToken("body").SelectToken("appIp")?.ToString();
                            mqStateMessage.Body.AppPort = mqttDatas.SelectToken("body").SelectToken("appPort")?.ToString();
                            mqStateMessage.Body.AppType = mqttDatas.SelectToken("body").SelectToken("appType")?.ToString();
                            mqStateMessage.Body.Key = mqttDatas.SelectToken("body").SelectToken("key")?.ToString();
                            mqStateMessage.Body.Event_time = mqttDatas.SelectToken("body").SelectToken("event-time")?.ToString();
                            if (!steData.ContainsKey(equipNo))
                            {
                                steData.Add(equipNo, mqStateMessage);
                                LogDebug($"插入设备上线数据成功...【End-At{DateTime.Now}】");
                                //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");

                            }
                            else
                            {
                                steData[equipNo] = mqStateMessage;
                                LogDebug($"更新设备上线数据成功...【End-At{DateTime.Now}】");
                                //LogDebug($"------------------------------------------------End------------------------------------------------<<<\n\n");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Utils.LogError(ex);
                }
            });
        }

        /// <summary>
        /// 与broker建立连接
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="user"></param>
        /// <param name="pwd"></param>
        private void Connect(string ip, int port, string user, string pwd)
        {
            try
            {
                var conn = client.ConnectAsync(new MqttClientOptionsBuilder()
                                 .WithTcpServer(ip, port)
                                 .WithCredentials(user, pwd)
                                 .Build());
                Utils.LogDebug($"connecting {ip}:{port}");
                conn.Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 订阅主题
        /// </summary>
        /// <param name="topic">主题</param>
        public void Subscribe(string topic)
        {
            try
            {
                var t = client.SubscribeAsync(topic);
                t.Wait();

                foreach (var item in t.Result.Items)
                {
                    if (item.ResultCode == MQTTnet.Client.Subscribing.MqttClientSubscribeResultCode.GrantedQoS0
                        || item.ResultCode == MQTTnet.Client.Subscribing.MqttClientSubscribeResultCode.GrantedQoS1
                        || item.ResultCode == MQTTnet.Client.Subscribing.MqttClientSubscribeResultCode.GrantedQoS2)
                    {
                        if (!_topicCache.Contains(item.TopicFilter.Topic))
                            _topicCache.Add(item.TopicFilter.Topic);
                    }
                    else
                    {
                        if (_topicCache.Contains(item.TopicFilter.Topic))
                            _topicCache.Remove(item.TopicFilter.Topic);
                    }
                }
            }
            catch (Exception ex)
            {
                Utils.LogError(ex);
                //if (_topicCache.Contains(topic))
                //    _topicCache.Remove(topic);
            }
        }
        /// <summary>
        /// 发送主题消息
        /// </summary>
        /// <param name="topic">主题</param>
        /// <param name="payload">消息体</param>
        public Task PublishAsync(string topic, string payload)
        {
            var message = new MqttApplicationMessageBuilder()
                .WithTopic(topic)
                .WithPayload(payload)
                .WithAtMostOnceQoS()
                .WithRetainFlag(false)
                .Build();

            return client.PublishAsync(message);
        }

        /// <summary>
        /// 是否已经订阅该主题
        /// </summary>
        /// <param name="equipno"></param>
        /// <param name="topic"></param>
        /// <returns></returns>
        internal bool IsSubscribed(string equipno, string topic)
        {
            var cmdTopic = $"{topic}{equipno}";
            return _topicCache.Contains(cmdTopic);
        }


        /// <summary>
        /// 根据设备号订阅主题
        /// </summary>
        /// <param name="targetEquipno"></param>
        /// <param name="isCmdRsp"></param>
        internal void SubscribeByEquipNo(string equipno, bool isCmdRsp = false)
        {
            var topic = "";
            if (isCmdRsp)
            {
                topic = $"{TopicString.DeviceActionResult}{equipno}";
                Subscribe(topic);
            }
            topic = $"{TopicString.DeviceState}{equipno}";
            Subscribe(topic);
        }



        private ReaderWriterLockSlim dataLogWriteLock = new ReaderWriterLockSlim();
        private readonly string dataFilePath = "../log/RHZD";
        private readonly DateTime dT = DateTime.Now;

        public void LogDebug(string msg)
        {
            try
            {
                dataLogWriteLock.EnterWriteLock();
                string fileName = $"../log/RHZD/{dT.Year}-{dT.Month}-{dT.Day}-Rhzd-DataLog.txt";
                if (!Directory.Exists(dataFilePath))
                {
                    //创建文件
                    Directory.CreateDirectory(dataFilePath);
                }
                using (StreamWriter writer = System.IO.File.AppendText(fileName))
                {
                    writer.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} --{msg}");
                }
            }
            catch (Exception ex)
            {
                DataCenter.WriteLogFile($"{System.Threading.Thread.CurrentThread.ManagedThreadId}>>>写入日志文件失败{ex.Message}");
                throw;
            }
            finally
            {
                dataLogWriteLock.ExitWriteLock();
            }
        }

    }
}